Skip to content

fix(test): make the sweep's flaky e2e and packed suites deterministic - #435

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
fix/sweep-deterministic-e2e-tests
Sep 3, 2026
Merged

fix(test): make the sweep's flaky e2e and packed suites deterministic#435
ScriptedAlchemy merged 2 commits into
mainfrom
fix/sweep-deterministic-e2e-tests

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Summary

Deterministic fixes for the e2e/packed suites reported flaky across lanes and CI. Every fix is at the source of the race (event-ordered waits, per-worker isolation, a product fix in the MCP App frame relay); no retries, no wall-clock padding, no raised timeouts.

Item Root cause Fix
mcp-page-app-browser close count (expected 8 to be 7, expected [] to equal ['/']) McpAppFrameRelay.close() posted ui/resource-teardown into a proxy iframe that had not signaled readiness (still loading / about:blank, whose origin never matches targetOrigin), so nothing could acknowledge it; the 5 s force-close timer then added a second close record whenever the test outlived the budget (the failing CI run took 6.97 s vs ~1.5 s). The sandbox-request assertion read the server log right after the iframe entered the DOM, before its document request reached the server. Relay: when the proxy never reported ready, force-close immediately instead of holding closing for the whole budget (packages/workbench/src/mcp/mcp-app-frame.tsx, unit test proves the 30 s budget is never armed). Test: wait on each binding's initialized / close route records and on the sandbox server's own request event; assert the exact per-binding close sequence.
mcp-app-real waitForRequest (Run → POST /api/runtime/apps) The create request is the last link of a chain (run admitted → run settled → succeeded run with App evidence mounts the preview → POST). A failed or evidence-less run surfaced only as a request that never arrived (30–120 s). Wait on each link in order and fail fast with run/stage diagnostics. The second /close wait now waits for the reopened app's ui/notifications/initialized first (a preview closed before its proxy loads is released by DELETE, by design after the relay fix).
dev-workbench "double config load" The fixture appended config-calls.ndjson and wrote provider-context.json into the watched project root. The dev watcher treats every non-ignored path as source (the project snapshot is broad by design), so each write was a source change → rebuild → prepare('dev') → config re-load → append → another rebuild: a self-sustaining loop that reached 12 loads in 2 s once the read was delayed. Fixture records moved outside the watched tree: provider context to its contract-provided storageRoot (.agent-bundle/runtime/<session>), config-call log beside the workbench assets. Assertion unchanged (exactly one dev load). Source behaves per its documented contract; no src change.
dev-workbench-packaging Aug 29 CI failure: pnpm build from inside the unit pool collided with another worker's build (Failed to generate declaration files). Since moved to the prebuilt packed pool by earlier PRs. Remaining hazard: its rslib build --dist-path <isolated> shares Rslib's persistent cache with packed-consumer's build (see Rspack item). installedEnvironment() on the spawned build → per-command cache directory.
packed-release.e2e "epoch mismatch" (expected 'a9dd…' to be 'd38a…' in phase invalid edit retains stale epoch B) Each source edit paired replaceWatchedSource with an immediate Overview Rebuild click, racing the watcher's own rebuild of the same write; whichever build landed last silently replaced the epoch later phases had pinned (the pattern replaceWatchedSourceAndAwaitRebuild already fixed for in-process servers). New replaceSourceAndAwaitWatcherRebuild: capture project_status attempt ids, replace, wait for the first completed attempt not known before the write, then click Rebuild. The two-file epoch C edit is sequenced.
public-api-packed 30 s timeouts isolatedCommandEnvironment() gave every spawned command a fresh npm cache, so every npm install <tarball> was cold: ~180 MB of registry tarballs per test (8.9 s cold vs 4.3 s offline here; a hosted runner's npm cache is empty at job start — pnpm/setup caches only the pnpm store — so Release gates and Verify pay it for every install). npm cache is now per worker (rstestWorkerNpmCacheDirectory), and the file warms it once in a beforeAll with its own 180 s budget (justified in the comment), so no per-test 30 s budget spans a cold download. --prefer-offline then serves everything from disk.
Rspack persistent cache Transaction already in progress Rslib's createConstantRsbuildConfig sets performance.buildCache: true; Rsbuild keys the directory as <config root>/node_modules/.cache/rspack, never by --dist-path. packed-consumer and dev-workbench-packaging both spawn rslib build --config packages/agent-bundle/rslib.config.ts from parallel packed-pool workers → one lock. Reproduced locally: 1 of 13 cold concurrent pairs failed with Transaction already in progress by process 2892811:node in directory '…/packages/agent-bundle/node_modules/.cache/rspack/esm-production/rspack_v_ef3dc523a56e4030/.temp'; 0 of 8 with isolated directories. packages/agent-bundle/rslib.config.ts honours AGENT_BUNDLE_RSLIB_CACHE_DIRECTORY; isolatedCommandEnvironment() sets it per command. pnpm build keeps the default warm cache.
Registry EATTESTATIONVERIFY (npm audit signatures) Genuine CI infra (CI run 33584654855: @modelcontextprotocol/server@2.0.0 failed to verify attestation: Unexpected end of JSON input). Retry policy documented in docs/local-ci.md; no code change.

Evidence

  • CI logs read: 400 failed ci.yml runs (2026-08-28 → 2026-09-03) plus 3 release.yml failures. Cited runs: 33734384098 (dev-workbench double load + 8-of-7 closes), 33735363205 ([] vs ['/']), 33420032167 (waitForRequest 30 s at the Run step), 33154508245 (packed-release epoch mismatch), 33232848276 (dev-workbench-packaging dts collision), 33584654855 (EATTESTATIONVERIFY).
  • Local reproduction before the fix: dev-workbench with a 2 s read delay → 12 dev config loads (after: exactly 1 with the same delay); new relay unit test on the old source → hangs on the 30 s timer (test timed out in 30000ms); concurrent rslib builds on a shared cold cache → 1/13 Transaction already in progress.
  • Proof loops after the fix (loaded machine, AGENT_BUNDLE_TEST_TIME_SCALE=4): dev-workbench runtime-once 10/10, mcp-page-app-browser 5/5 (+12/12 under 4-way concurrent stress and 3 CPU-throttled runs earlier), mcp-app-real 3/3, packed-release 3/3, public-api-packed + dev-workbench-packaging + packed-consumer 3/3 pool runs.

Test plan

  • pnpm typecheck
  • pnpm lint
  • pnpm test:unit — 3002 passed, 1 failed: native-claude-contract.test.ts › fails closed when the candidate plugin, exact Skill event, or subscription auth source is absent (test timed out in 5000ms). Pre-existing: fails identically on a pristine origin/main checkout in this environment; untouched by this PR.
  • pnpm build && pnpm test:integration:run
  • pnpm test:packed (touched files) — 8/8

Review status

  • CI: all checks green on head 308b245e8 (Verify Node 24, Release gates, Host install proofs, Examples check, RSC runtime micro-eval, Docs site, Changeset present, Dependency review).
  • chatgpt-codex-connector: three requests (automatic on open, @codex review at 18:50 UTC and 19:07 UTC) all answered "You have reached your Codex usage limits for code reviews"; zero review threads.
  • Last codex-reviewed SHA: none. Unreviewed SHAs: 133361eab (the fix), 308b245e8 (changeset PR reference). Merged on green CI per the lane policy; any later codex threads will be answered in a follow-up PR.

mcp-page-app-browser (8-of-7 closes): the MCP App frame relay posted its
teardown into a proxy document that had not signaled readiness, so nothing
could acknowledge it and the 5 s force-close timer added a second close
record whenever the test outlived the budget. The relay now force-closes
immediately when the proxy never reported ready (no window can answer), and
the test waits on each binding's initialized/closed lifecycle records and
the sandbox server's own request event instead of DOM presence.

mcp-app-real: the Run -> /api/runtime/apps wait now sequences run admission,
run settlement, and the create request, failing fast with run diagnostics;
the second /close wait first waits for the reopened app to initialize.

dev-workbench (double config load): the fixture wrote its config-call log
and provider context into the watched project root, so every load was a
source change that rebuilt and re-loaded the config. Both records now live
outside the watched tree (provider storageRoot; assets root).

packed-release (epoch mismatch): source edits now wait for the packed dev
server's own watcher rebuild via project_status attempt ids before the
Overview Rebuild click, so one edit is one build and no late epoch replaces
the one later phases pinned.

public-api-packed (30 s timeouts): npm caches are now per worker rather
than per command, and the file warms the cache once in a budgeted beforeAll,
so no per-test budget spans a cold ~180 MB registry download.

Rspack persistent cache ("Transaction already in progress"): Rslib enables
the persistent cache by default and keys it by config root, so two test
workers rebuilding packages/agent-bundle into isolated dists shared one lock.
Spawned builds now get a per-command cache directory via
AGENT_BUNDLE_RSLIB_CACHE_DIRECTORY.

docs/local-ci.md records the retry policy for the registry-side
EATTESTATIONVERIFY failure of `npm audit signatures`.
@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 308b245

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@435
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@435
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@435

commit: 308b245

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant